﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.ComponentModel;
using IGolovkine.Core.Diagnostics;

namespace IGolovkine.ShoutCastRipper
{
    public class ContentFileRepository : IContentRepository
    {
        private const string IncompleteTag = "[Incomplete] - ";

        private readonly string _storeFolder;
        private readonly IEnumerable<string> _lookUpFolders;

        private FileStream _fileStream;
        private string _currentStreamTitle;
        private string _currentFileName;

        public ContentFileRepository(string storeFolder, IEnumerable<string> lookUpFolders)
        {
            Debug.NotNullOrEmpty(() => storeFolder);
            Debug.NotDefault(()=> lookUpFolders);

            _storeFolder = storeFolder;

            _lookUpFolders = lookUpFolders.Union(new string[] { storeFolder });
        }

        public bool SavingContent
        {
            get { return _fileStream != null; }
        }

        public void BeginStoreContent(string streamTitleName)
        {
            _currentStreamTitle = streamTitleName;
            if (_fileStream == null)
            {
                if (Directory.Exists(_storeFolder) == false)
                {
                    Directory.CreateDirectory(_storeFolder);
                }

                if (_currentStreamTitle == null)
                {
                    throw new InvalidOperationException("Cannot create a file, no Stream Title received");
                }

                string filePath = AllocateFileName(_storeFolder, _currentStreamTitle, true);

                _fileStream = new FileStream(filePath, FileMode.CreateNew);
                _currentFileName = filePath;
            }
            else
            {
                throw new InvalidOperationException("Exising writing session is not closed");
            }
        }

        public void AppendContent(byte[] data)
        {
            if (_currentStreamTitle == null)
            {
                throw new IOException("Couldn't get stream header");
            }
            if (_fileStream != null)
            {
                _fileStream.Write(data, 0, data.Length);
            }
            else
            {
                throw new InvalidOperationException("Cannot write to a file, no Stream Title received");
            }

        }

        public void CancelStoreContent()
        {
            if (_fileStream != null)
            {
                string fileName = _currentFileName;
                this.EndStoreContent(false);
                File.Delete(fileName);
            }
            else
            {
                throw new InvalidOperationException("No content to cancel");
            }
        }
        
        public void EndStoreContent(bool isComplete)
        {
            if (_fileStream != null)
            {
                _fileStream.Dispose();
                _fileStream = null;
                
                if (isComplete)
                {
                    this.CompleteCurrentStreamFile();
                }
                _currentStreamTitle = null;
                _currentFileName = null;
            }
            else
            {
                throw new InvalidOperationException("No content to end");
            }
        }

        public string CurrentFileName
        {
            get { return _currentStreamTitle; }
        }

        private void CompleteCurrentStreamFile()
        {
            string title = GetContentTitleFromFileName(Path.GetFileName(_currentFileName));
            string newPath = AllocateFileName(_storeFolder, title, false);
            File.Move(_currentFileName, newPath);
            _currentFileName = newPath;
        }

        public static bool IsIncomplete(string fileName)
        {
            string fileNameWithoutExt = Path.GetFileNameWithoutExtension(fileName);
            int searchResut = fileNameWithoutExt.IndexOf(IncompleteTag);
            return searchResut != -1;
        }

        private static string AllocateFileName(string destPath, string streamTitle, bool markIncomplete)
        {
            if (!Directory.Exists(destPath))
            {
                Directory.CreateDirectory(destPath);
            }
            string incompleteTag = markIncomplete ? IncompleteTag : string.Empty;
            if (!File.Exists(Path.Combine(destPath, incompleteTag + streamTitle + ".mp3")))
            {
                return Path.Combine(destPath, incompleteTag + streamTitle + ".mp3");
            }
            else
            {
                for (int i = 1; ; i++)
                {
                    if (!File.Exists(Path.Combine(destPath, incompleteTag + streamTitle + " (" + i + ").mp3")))
                    {
                        return Path.Combine(destPath, incompleteTag + streamTitle + " (" + i + ").mp3");
                    }
                }
            }
        }

        public static string GetContentTitleFromFileName(string filename)
        {
            return Path.GetFileNameWithoutExtension(filename).Replace(IncompleteTag, string.Empty);
        }

        public string GetContentLocation(string title)
        {
            foreach (string lookUpFolder in _lookUpFolders)
            {
                if (Directory.Exists(lookUpFolder))
                {
                    foreach (string fileName in Directory.GetFiles(lookUpFolder, "*.mp3"))
                    {
                        string storedTitle = GetContentTitleFromFileName(fileName);
                        if (IsIncomplete(fileName) == false && string.Compare(title, storedTitle, true) == 0)
                        {
                            return fileName;
                        }
                    }
                }
            }

            return null;
        }

        public void Dispose()
        {
            if (_fileStream != null)
            {
                _fileStream.Dispose();
            }
        }
    }
}
